﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

using SharedGenomics.Core;

namespace SharedGenomics.Domain
{
    [DataContract]
    public class NominalFileSchemaColumn : FileSchemaColumn
    {
        #region Constructors

        public NominalFileSchemaColumn()
            : base(ColumnDistribution.Nominal)
        {
        }

        protected NominalFileSchemaColumn(ColumnDistribution type)
            : base(type)
        {
        }

        #endregion

        [OnDeserializing]
        internal void OnDeserialising(StreamingContext context)
        {
            this.Initialize();
        }

        #region Public Methods

        public void AddValue(string value)
        {
            this.AddValue(value, null, false);
        }

        public void AddValue(string value, string label)
        {
            this.AddValue(value, label, false);
        }

        public void AddMisingValue(string value)
        {
            this.AddValue(value, null, true);
        }

        public override void Validate(string value, ValidationMessageCollection validationMessages)
        {
            if (!(this.IsMissing(value) || this.FindByValue(value.Trim()) != null))
            {
                validationMessages.AddWarningMessage(String.Format("Unexpected value '{0}' in column '{1}'", value, this.Label ?? this.Name));
            }
        }

        public override object Parse(string value)
        {
            if (this.IsMissing(value))
            {
                return null;
            }
            else
            {
                string trimmedValue = value.Trim();
                Nominal existingValue = this.FindByValue(value);
                return existingValue ?? new Nominal(trimmedValue, null);
            }
        }

        #endregion

        #region Private Methods

        private bool IsMissing(string value)
        {
            //if a value is empty it is considered missing, even though it is not explicitly included in the list of missing values
            return value.IsEmptyOrWhitespace() || this.MissingValues.Contains(value.Trim());
        }

        private Nominal FindByValue(string value)
        {
            return this.Values.Where(n => String.Equals(n.Value, value, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
        }

        #endregion

        #region Properties

        public bool IsBinary
        {
            get
            {
                return this.Values.Count() == 2 &&
                    this.Values.Count(n => n.Value.In("0", "1")) == 2;
            }
        }

        public override bool IsValidPhenotypeColumn
        {
            get { return this.IsBinary; }
        }

        public virtual IEnumerable<Nominal> Values
        {
            get
            {
                return from cv in this.FileSchemaColumnValues
                       where !cv.IsMissing
                       select new Nominal(cv.Value, cv.Label);
            }
        }

        public IEnumerable<string> MissingValues
        {
            get { return this.MissingValuesCore; }
        }

        #endregion
    }
}
